---
title: SQL 语句学习
date: 2023-05-22
images: ['https://kodo.huixiangwuyou.com/blog/images/sql.png']
summary: '不管是MYSQL、MongoDB、Oracle还是其他数据库，SQL语句都是统一的规范。只要掌握了sql语言都可以通用'
tags:
 - SQLDB
---

## SQL

> 在数据库中，SQL（Structured Query Language）是一种用于管理和操作关系型数据库的标准语言。它提供了一种结构化的方式来查询、更新和管理数据。SQL语句可以用于执行各种操作，包括数据检索、数据插入、数据更新和数据删除。

### DQL查询
DQL（Data Query Language）是一种用于查询数据库中数据的语言。它是SQL语言的一个子集，主要用于从数据库中检索数据。DQL语句用于从数据库中检索数据，而不涉及对数据的修改。我们对数据库操作最多的是查询，而不是新增修改。

```sql
# 查询多个字段
SELECT column1, column2, ... FROM table_name;
# 查询所有字段
SELECT * FROM table_name;

# 查询指定条件的数据
SELECT column1, column2,... FROM table_name WHERE condition;

# 别名
SELECT column1 AS alias_name, column2 AS alias_name,... FROM table_name;
SELECT column1 alias_name, column2 alias_name,... FROM table_name; # 别名可以省略AS

# 去重
SELECT DISTINCT column1, column2,... FROM table_name;

```

> 条件查询
```sql
# 等于
SELECT * FROM table_name WHERE column1 = value;
# 不等于
SELECT * FROM table_name WHERE column1 != value;
# 大于
SELECT * FROM table_name WHERE column1 > value;
# 小于
SELECT * FROM table_name WHERE column1 < value;
# 大于等于
SELECT * FROM table_name WHERE column1 >= value;
# 小于等于
SELECT * FROM table_name WHERE column1 <= value;
# between  BETWEEN value1 AND value2, value1必须为最小值，value2必须为最大值，否则无法查询结果
SELECT * FROM table_name WHERE column1 BETWEEN value1 AND value2;
# 模糊查询
SELECT * FROM table_name WHERE column1 LIKE '%value%';
# 模糊查询，%表示任意多个字符，_表示任意单个字符
SELECT * FROM table_name WHERE column1 LIKE 'value%';
SELECT * FROM table_name WHERE column1 LIKE '%value';
SELECT * FROM table_name WHERE column1 LIKE '_value';
SELECT * FROM table_name WHERE column1 LIKE 'value_value';


# 多个条件查询
SELECT * FROM table_name WHERE column1 = value1 AND column2 = value2;
SELECT * FROM table_name WHERE column1 = value1 OR column2 = value2;
SELECT * FROM table_name WHERE age = 20 OR age = 18 OR age = 16;
SELECT * FROM table_name WHERE age IN (20,18,16); # IN表示在多个值中
SELECT * FROM table_name WHERE age NOT IN (20,18,16); # NOT IN表示不在多个值中
SELECT * FROM table_name WHERE age BETWEEN 18 AND 20; # BETWEEN表示在两个值之间
SELECT * FROM table_name WHERE age NOT BETWEEN 18 AND 20; # NOT BETWEEN表示不在两个值之间
SELECT * FROM table_name WHERE column1 = value1 AND column2 = value2 OR column3 = value3;
SELECT * FROM table_name WHERE column1 = value1 AND (column2 = value2 OR column3 = value3);
SELECT * FROM table_name WHERE column1 = value1 AND column2 = value2 AND column3 = value3;

```

#### 聚合函数
聚合函数是一种特殊的函数，它可以对一组数据进行汇总计算。聚合函数通常用于查询数据的统计信息，例如求和、平均值、最大值、最小值等。

```sql
# 求和
SELECT SUM(column1) FROM table_name;
# 平均值
SELECT AVG(column1) FROM table_name;
# 最大值
SELECT MAX(column1) FROM table_name;
# 最小值
SELECT MIN(column1) FROM table_name;
# 计数
SELECT COUNT(column1) FROM table_name;
# 计数，不包括NULL值
SELECT COUNT(column1) FROM table_name WHERE column1 IS NOT NULL;
# 计数，包括NULL值
SELECT COUNT(column1) FROM table_name WHERE column1 IS NULL;
# 计数，包括重复值
SELECT COUNT(column1) FROM table_name;
# 计数，不包括重复值
SELECT COUNT(DISTINCT column1) FROM table_name;
# 计数，包括重复值和NULL值
SELECT COUNT(*) FROM table_name;
# 计数，不包括重复值和NULL值
SELECT COUNT(DISTINCT column1) FROM table_name WHERE column1 IS NOT NULL;
# 计数，包括重复值和NULL值
SELECT COUNT(*) FROM table_name WHERE column1 IS NOT NULL;
# 计数，不包括重复值和NULL值
SELECT COUNT(DISTINCT column1) FROM table_name WHERE column1 IS NULL;

```
> 在统计聚合函数数量的时候是不统计NULL值的，但是如果想要统计NULL值，可以使用COUNT(*)或者COUNT(1)。

#### 分组查询
分组查询是一种特殊的查询，它可以对一组数据进行分组，并对每个分组进行汇总计算。分组查询通常用于查询数据的统计信息，例如求和、平均值、最大值、最小值等。
```sql
SELECT column1, column2,... FROM table_name GROUP BY column1, column2,...;
SELECT column1, column2,... FROM table_name GROUP BY column1, column2,... HAVING condition;

SELECT gender count(*) FROM table_name GROUP BY gender;
# 统计男女性别下的> 35 人数 
SELECT gender count(*) FROM table_name GROUP BY gender HAVING count(*) > 35;

```
分组之后，查询的字段一般为聚合函数和分组字段，查询其他字段无任何意义。where和having的区别：
- where是在分组前进行过滤，having是在分组后进行过滤。
- where不能使用聚合函数，having可以使用聚合函数。
- where的执行顺序比having先执行。


#### 排序查询
排序查询是一种特殊的查询，它可以对一组数据进行排序。排序查询通常用于查询数据的统计信息，例如求和、平均值、最大值、最小值等。
```sql
SELECT column1, column2,... FROM table_name ORDER BY column1, column2,... ASC|DESC;
SELECT *age.* FROM table_name ORDER BY age ASC|DESC;
# ASC表示升序，DESC表示降序,默认是升序 可以省略ASC
SELECT * FROM table_name ORDER BY age;

# 排序第一个相同的字段，第二个字段排序可以另外设计
SELECT * FROM table_name ORDER BY age, name;

```



#### 分页查询
分页查询是一种特殊的查询，它可以对一组数据进行分页。分页查询通常用于查询数据的统计信息，例如求和、平均值、最大值、最小值等。
```sql
SELECT column1, column2,... FROM table_name LIMIT offset, length;
SELECT column1, column2,... FROM table_name LIMIT length OFFSET offset;
SELECT column1, column2,... FROM table_name LIMIT length;

# 查询第1页，每页10条数据
SELECT * FROM table_name LIMIT 0, 10;
SELECT * FROM table_name LIMIT 10 OFFSET 0;
SELECT * FROM table_name LIMIT 10;

# 查询第2页，每页30条数据  起始索引=(查询页码-1)*每页显示记录数
SELECT * FROM table_name LIMIT 60, 30;

```
**注意**
- 起始索引从0开始，起始索引=(查询页码-1)*每页显示记录数
- 分页查询是数据库的方言，不同的数据库有不同的实现，MySQL中是UMIT
- 如果查询的是第一页数据，起始索引可以省略，直接简写为imit 10。
- 进行分页时，排序的sql语句必须在limit语句之前。


#### 执行顺序
编写顺序：select -> from -> where -> group by -> having -> order by -> limit
执行顺序：from -> where -> group by -> having -> select -> order by -> limit
```sql
SELECT column1, column2,... FROM table_name WHERE condition GROUP BY column1, column2,... HAVING condition ORDER BY column1, column2,... LIMIT offset, length;

# 别名验证 其执行结果都是一样的
select nan,age from enp where age > 15 order by age asc;
select nan,age from enp e where e.age > 15 order by age asc;
select e.nan,e.age from enp e where e.age > 15 order by age asc;
# 解释：
# 1. from enp e 表示从enp表中查询数据，别名e 
# 2. where e.age > 15 表示查询年龄大于15的员工
# 下面执行报错 说明select是where前执行的
select e.nan enan,e.age eage from enp e where eage > 15 order by age asc;

# 下面执行成功，说明select是order by前执行的
select e.nan enan,e.age eage from enp e where e.age > 15 order by eage asc;


```



### 函数
函数是一种特殊的语句，它可以对一组数据进行计算。函数通常用于查询数据的统计信息，是SQL数据库内置函数。

#### 字符串函数
| 函数 | 功能 |
| --- | --- |
| CONCAT(S1,S2..Sn) | 字符串3拼接，将S1，S2，….Sn拼接成一个字符串 |
| LOWER(str) | 将字符f串str全部转为小写 | 
| UPPER(Str) | 将字符串str全部转为大写 |
| LPAD(str,n,pad) | 左填充，用字符串pad对str的左边进行填充，达到n个字符串长度 |
| RPAD(str,n,pad) | 右填充，用字符串pad对str的右边进行填充，达到n个字符串长度 |
| TRIM(str) | 去掉宇2符串头部和尾部的空格 | 
| SUBSTRING(str,start,len) | 返回从字符串str从start位置起的len个长度的字符串 |
|  |  |  

```
# LOWER
SELECT LOWER('Hello World'); # hello world
# UPPER
SELECT UPPER('Hello World'); # HELLO WORLD

# 由于业务变更、企业员工的工号，统一为5位数，目前不足5位数的全部企的围补0。比如:1号员工的工号应读为00001
UPDATE employee SET id = LPAD(id,5,'0');
```

#### 数值函数
| 函数 | 功能 |
| --- | --- |
| CEIL(x) | 向上取整 |
| FLOOR(x) | 向下取整 |
| MOD(x,y) | 返回x/y的模(取除数的余) |
| RAND() | 返回0~1内的随机数 |
| ROUND(X，y) | 求参数x的四舍五入的值，保留y位小数 |
|  |  |

通过数据库的的数，生成一个六位数的随机验证码。
```sql
SELECT lpad(ROUND(RAND()*1000000,0),6,'0'); # 左填充
SELECT RPAD(ROUND(RAND()*1000000,0),6,'0');# 右填充
```
#### 日期函数
| 函数 | 功能 |
| --- | --- |
| CURDATE() | 返回当前日期 |
| CURTIME() | 返回当前时间 |
| NOW() | 返回当前系统日期+时间 |
| YEAR(date) | 获取指定date的年份 |
| MONTH(date) | 获取指定date的月份 |
| DAY(date) | 获取指定date的日期 |
| DATE_ADD(date, INTERVAL exp type) | 返回一个日期/时间值加上一个时间间隔expr后的时间值 |
| DATEDIFF(date1,date2) | 返回起治时间date1 和 结束时间date2之间的天数 |
|  |  |


```
# 查看当前日期
SELECT CURDATE(); # 2023-05-22
# 查看当前时间
SELECT CURTIME(); # 18:34:50
# 查看当前系统日期+时间
SELECT NOW(); # 2023-05-22 18:34:50
# 获取指定date的年份
SELECT YEAR('2023-05-22'); # 2023
# 获取指定date的月份
SELECT MONTH('2023-05-22'); # 5
# 获取指定date的日期
SELECT DAY('2023-05-22'); # 22
# 返回一个日期/时间值加上一个时间间隔expr后的时间值
SELECT DATE_ADD('2023-05-22', INTERVAL 1 DAY); # 2023-05-23 
SELECT DATE_ADD('2023-05-22', INTERVAL 1 YEAR); # 2024-05-22 

# 返回起治时间date1 和 结束时间date2之间的天数
SELECT DATEDIFF('2023-05-22', '2023-05-23'); # -1

# 查询所有员工的入期天数，并根据入期天数倒序排序。
SELECT name, DATEDIFF(NOW(), enter_time) AS days FROM employee ORDER BY days DESC;
```

#### 流程控制函数
| 函数 | 功能 |
| --- | --- |
| IF(value,t,f) | 如果value为true，则返回t，否则返回f |
| IFNULL(value1 ,value2) | 如果value1不为空，返回value1，否则返回value2|
| CASE WHEN [val1 ] THEN [res1] ... ELSE [ default ] END | 如果val1为true，返回res1，.否则返回默认值 |
| CASE [ expr] WHEN [ val1 ] THEN [res1] ... ELSE [ default ] END | 如果expr的值等于val1，返回res1，…否则返回默认值|
|  |  | 

开发的emp表的员工姓名和工作地址(北京/上海-一线城市 ，其他-二线城市)
```sql
SELECT name, IF((city = '北京' or city = '上海' ), '一线城市', '二线城市') AS address FROM employee;
SELECT name, CASE city WHEN '北京' THEN '一线城市' WHEN '上海'  THEN '一线城市' ELSE '二线城市' END AS address FROM employee;
```

#### 实战检测
查询性别男，年龄在18-20（含）之间姓名在3个字的学生信息
```sql >folded
SELECT * FROM student WHERE gender = '男' AND age BETWEEN 18 AND 20 AND name LIKE '___';
```
<br/>
统计员王表中，年龄小于60岁的，男性员工和女性员工的人数。
```sql >folded
SELECT gender, COUNT(*) FROM employee WHERE age < 60 GROUP BY gender;
```
<br/>
查询所有年龄小于等于35岁员正的姓名和年龄，并对查询结果按年龄开序播序，如果年龄相同按入眼时间降序排序。
```sql >folded
SELECT name, age FROM employee WHERE age <= 35 ORDER BY age ASC, enter_time DESC;
```
<br/>
查询性别为男，且年龄在20-48 岁(含)以内的前5个员工信息，对查询的结果按年龄开序播序，年龄相同按入眼时间开序播序。
```sql >folded
SELECT * FROM employee WHERE gender = '男' AND age BETWEEN 20 AND 48 ORDER BY age ASC, enter_time ASC LIMIT 5;
```
<br/>
统计学生分数 >= 85  优秀 >=60 及格  不及格。
```sql >folded
SELECT name, CASE WHEN score >= 85 THEN '优秀' WHEN score >= 60 THEN '及格' ELSE '不及格' END AS score FROM student;
```

### 约束
概念:约束是作用于表中字段上的规则，用于限制存储在表中的数据。   
目的:保证数据库中数据的正确、有效性和完整性。
| 关键字 | 约束 | 描述 |
| --- | --- | --- |
| NOT NULL | 非空约束 | 限制该字段的数据不能为null |
| UNIQUE | 唯一约束 | 保证该字段的所有数据都是唯一、不重复的 |
| PRIMARY KEY | 主键约束 |主键是一行数据的唯一标识，要求非空且唯一 |
| DEFAULT | 默认约束 | 保存数据时，如果未指定该字段的值，则采用默认值 |
| CHECK | 检查约束(8.0.16版本之后) | 保证字段值满足某一个条件 |
| FOREIGN KEY | 外键约束 | 用来让两张表的数据之间建立连接，保证数据的一致性和完整性 |
|  |  |  |

案例展示
| 字段名 | 字段含义 | 字段类型 | 约束条件 | 约束关键字 |
| --- | --- | --- | --- | --- |
| id | ID唯一标识 | 取整 | 主键，并且自动增长 | PRIMARY KEY, AUTO INCREMENT |
| 姓名 | 姓名 | varchar(10) | 不为空，并且唯一 | NOT NULL, UNIQUE |
| 年龄 | 年龄 | int | 大于0，并且小于等于120 | CHECK |
| 状态 | 状态 | char(1) | 如果没有指定该值，默认为1 | DEFAULT |
| 性别 | 性别 | char(1) | 无 | --- |
|  |  |  |  |  |
```sql
# 创建一个博客评论表
CREATE TABLE comments (
  id INT AUTO_INCREMENT PRIMARY KEY,          -- 评论 ID，主键
  content TEXT NOT NULL,                      -- 评论内容
  username VARCHAR(100) NOT NULL UNIQUE,             -- 评论者用户名（冗余字段，避免频繁查询用户表）
  status ENUM('pending', 'approved', 'deleted') DEFAULT 'pending', -- 状态
  gender char(1), 
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 评论更新时间
  metadata JSON                               -- 扩展字段（JSON 格式，支持灵活扩展）
) comment '用户表';
```

#### 外键约束
- 外键约束是作用于多张表的字段上的规则，用于限制存储在表中的数据。
- 外键约束的作用是保证两张表的数据之间建立连接，保证数据的一致性和完整性。
- 外键约束的关键字是FOREIGN KEY。

```sql
create table dept(
  id int auto increment conment "ID' prinary key,
  name varchar(58)not null comment'部门名称'
) connent'部们表";

INSERT INTO dept (id,name) VALUES(1,'研发部”)，(2,'市场'),(3,'财务郞')，(4，'请售')，(5,'总经办');


create table emp(
  id int auto_increment comment 'ID' primary key,
  name varchar(50) not null comment '姓名',
  age int comment '年龄',
  job varchar(20) comment '职位',
  salary int comment'薪资',
  entrydate date comment '入职时间',
  managerid int comment'直属领导ID',
  dept_id int comment'部门ID
)comment'员工表";

INSERT INT0 emp (id, name, age, job, salary, entrydate, managerid, dept_id) VALUES
  (1, '金庸', 66, '总裁','2000日', '2000-01-01', null, 5),
  (2, '张无忌', 20, '项目经理', '1250日', '2005-12-05', 1, 1),
  (3, '杨道', 33, '开发'，'840日', '2000-11-03', 2, 1),
  (4, '韦一笑', 48, '开发', '1100日', '2002-02-05', 2, 1),
  (5, '常遇春', 43, '开发', '1058日', '2004-09-07', 3, 1),
  (6, '小昭', 19, '程序员鼓励师', '660日', '204-10-12', 2, 1);

```
外键约束的语法格式：
```sql
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (字段名) REFERENCES 主表名 (字段名);

# 创建的时候约束
CREATE TABLE table_name(
  ...
  CONSTRAINT fk_name FOREIGN KEY (column_name) REFERENCES other_table(column_name)
) ;

# 添加外键
alter table emp add constraint fk_enp_dept_id foreign key (dept_id) references dept(id);

# 删除外键
alter table emp drop foreign key fk_enp_dept_id;
```

| 行为 | 说明 |
| --- | --- |
| NO ACTION | 当在父表中制除/更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许除/更新。(与RESTRICT一致) |
| RESTRICT | 当在父表中剧除/更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许除/更新。(与NOACTON一致) | 
| CASCADE | 当在父表中剧除/更新对应记录时，首先检查该记录是否有对应外键，如果有，则也除/更新外健在子表中的记录。 |
| SET NULL | 当在父表中剧除对应记录时，首先检查该记录是否有对应外健，如果有则设置子表中该外健值为nul(这就要求该外健允许取nul)。|
| SET DEFAULT | 父表有变更时，子表将外键列设置成一个默认的值(lnnodb不支持) |
|  |  |  

```sql
# 外键约束的语法格式：
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (字段名) REFERENCES 主表名 (字段名) ON (update|DELETE) (NO ACTION|RESTRICT|CASCADE|SET NULL|SET DEFAULT);
```


### 多表查询
多表查询是指在一个查询中同时查询多个表的数据。多表查询可以通过连接查询、子查询、联合查询等方式实现。
多表关系：
- 一对一：一个学生只能有一个身份证，一个身份证只能属于一个学生。常用于单表拆分，例如用户信息表和用户详情扩展信息表。加入的主键要做unique约束。
- 一对多：一个部门可以有多个员工，一个员工只能属于一个部门。
- 多对多：一个学生可以选修多个课程，一个课程可以被多个学生选修。要建立一张关联表维护学生主键和课程主键之间的关系。


```sql
# 多表查询 笛卡尔积
SELECT * FROM student, course;
# 消除笛卡尔积 将关联的约束主键关联
SELECT * FROM student, course WHERE student.id = course.student_id;
```
#### 多表查询分类
```sql
# 内连接查询：内连接查询是指在查询结果中只包含满足连接条件的数据。内连接查询的语法格式如下：
SELECT * FROM table1 INNER JOIN table2 ON table1.column1 = table2.column2;

# 外连接查询：外连接查询是指在查询结果中包含不满足连接条件的数据。外连接查询的语法格式如下：
SELECT * FROM table1 LEFT JOIN table2 ON table1.column1 = table2.column2; # 左连接 查询左边的表和右边表关联交集的数据 展示左表所有数据
SELECT * FROM table1 RIGHT JOIN table2 ON table1.column1 = table2.column2; # 右连接 查询左边的表和右边表关联交集的数据 展示右表所有数据

# 自查询 看成两张表 例如:员工表和员工领导都是在一张表内，查询员工的姓名和领导的姓名
SELECT a.name , b.name FROM table1 a, table2 b WHERE a.column1 = b.column2;

# 子查询：子查询是指在查询语句中嵌套另一个查询语句。子查询的语法格式如下：
SELECT * FROM table1 WHERE column1 > (SELECT column2 FROM table2);
SELECT * FROM table1 WHERE column1 (IN|NOT IN|ANY|SOME|ALL) (SELECT column2 FROM table2);
SELECT * FROM table1 where (salary,managerid) = (select salary, managerid fron table1 where nane = '张元');
select * from enp where (job,salary) in ( select job, salary fron table1 where name='张三' or nane ='李氏') ;
select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept id = d.id ;

# 联合查询：联合查询是指将多个查询结果合并成一个结果集。联合查询的语法格式如下：
SELECT * FROM table1 UNION ALL SELECT * FROM table2;

# 联合查询去重：联合查询去重是指将多个查询结果合并成一个结果集，并去除重复的数据。联合查询去重的语法格式如下：
SELECT * FROM table1 UNION DISTINCT SELECT * FROM table2;

# 联合查询排序：联合查询排序是指将多个查询结果合并成一个结果集，并按照指定的字段进行排序。联合查询排序的语法格式如下：
SELECT * FROM table1 UNION SELECT * FROM table2 ORDER BY column1;

# 联合查询分页：联合查询分页是指将多个查询结果合并成一个结果集，并按照指定的字段进行分页。联合查询分页的语法格式如下：
SELECT * FROM table1 UNION SELECT * FROM table2 LIMIT 10, 10;

# 联合查询分组：联合查询分组是指将多个查询结果合并成一个结果集，并按照指定的字段进行分组。联合查询分组的语法格式如下：
SELECT * FROM table1 UNION SELECT * FROM table2 GROUP BY column1;

# 联合查询过滤：联合查询过滤是指将多个查询结果合并成一个结果集，并按照指定的字段进行过滤。联合查询过滤的语法格式如下：
SELECT * FROM table1 UNION SELECT * FROM table2 WHERE column1 = value;
```

> 联合查询，多用于自查询。
> 联合查询，多张表的字段可以必须一致，查询的字段和类型也必须一致。



#### 多表实战
查询每一个员工的姓名，及关联的部门的名称(隐式内连接实现)
```sql >folded
SELECT employee.name, dept.dept_name FROM employee, dept WHERE employee.dept_id = dept.id;
SELECT e.name, d.dept_name FROM employee e, dept d WHERE e.dept_id = d.id; # 使用别名后where条件必须使用表名
```
查询每一个员工的姓名，及关联的部门的名称(显式内连接实现)
```sql >folded
SELECT * FROM employee INNER JOIN dept ON employee.dept_id = dept.id;
```
将薪资大于8000的员工的姓名和年龄大于35的查询出来
```sql >folded
SELECT * FROM employee WHERE salary > 8000 AND age > 35;
SELECT * FROM employee WHERE salary > 8000 AND age > 35;
SELECT * FROM employee WHERE salary > 8000 UNION SELECT * FROM employee WHERE age > 35;
```

### 事务
事务 是一组操作的集合，它是一个不可分割的工作单位，事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求，即这些操作要么同时成功，要么同时失败。
例如：银行转账，从A账户转1000元到B账户，那么A账户就要减少1000元（前提是A有1000元），B账户就要增加1000元，这两个操作必须同时成功，要么都成功，要么都失败。

转账操作
```sql 
# 查询张三账户余额
select * fron account  where nane =张三;
# 张三账户余额减少1000元
update account set money =money-1000 where name ='张三';
# 李四账户余额增加1000元
update account set money =money+1000 where name ='李四';
```
事务操作
```sql
# 方式1
# 查看事务状态
show variables like 'autocommit'; # 1 自动提交 0 手动提交
select @@autocommit; # 1 自动提交 0 手动提交
# 关闭自动提交
set @@autocommit = 0;

# 提交事务
commit;
# 回滚事务
rollback;

# 方式2
# 查看事务状态
select @@transaction_isolation; # REPEATABLE-READ 可重复读

# 开启事务
start transaction; # 或者 begin;
# .....
# 提交事务
commit;
# 回滚事务
rollback;

```

> 方式一在修改事务状态是临时修改，服务器重启后会恢复默认状态，或在关闭数据库连接恢复。在关闭自动提交后，任何修改数据库的sql语句在执行后都需要commit提交事务修改数据库。在未提交事务和回滚事务不会影响数据库数据。


#### 事务的特性

- 原子性：事务是不可分割的最小操作单位，事务中的所有操作要么同时成功，要么同时失败。
- 一致性：事务完成时，必须使所有的数据都保持一致状态。
- 隔离性：多个事务之间是相互隔离的，这是多个事务并发执行时，数据库为每一个事务开启的，不能被其他事务操作的数据，多个事务之间不能互相干扰，也就是说一个事务不能看到其他事务未提交的数据。
- 持久性：事务一旦提交或回滚后，数据库会把数据持久化到磁盘上。

<br/>

#### 并发事务问题
| 分类问题 | 描述 |
| --- | --- | 
| 脏读 | 一个事务读到另外一个事务还没有提交的数据。 |
| 不可重复读 | 一个事务先后读取同一条记录，但两次读取的数据不同，称之为不可重复谈。 | 
| 幻读 | 一个事务按照条件查询数据时，没有对应的数据行，但是在插入数据时，又发现这行数据已经存在，好像出现了"幻影"。例如：<br/> - 事务A在查询id主键没有查到数据，事务B插入了id的数据 <br/> - 事务A插入id的数据，导致不可重复读录入数据失败 <br/> - 事务A再次查询id发现还是没有查到数据 |
|  |  |  | 


#### 事务的隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
| --- | --- | --- | --- |
| READ UNCOMMITTED | &check; |  &check; |  &check; |
| READ COMMITTED | &times; | &check; | &check; |
| REPEATABLE READ | &times; | &times; | &check; |
| SERIALIZABLE | &times; | &times; | &times; |
|  |  |  |  |

```sql
SELECT @@TRANSACTION_ISOLATION;
--设置当前会话事务隔离级别
SET [ SESSIONIGLOBAL] TRANSACTION ISOLATON LEVEL [ READ UNCOMMITED | READ COMMITED | REPEATABLE READ | SERALIZABLE ]
```